package net.openvision.music.composition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.openvision.music.composition.MusicalTexturer.ElementTree.Node;
import net.openvision.music.composition.rules.ConstructionRule;
import net.openvision.music.composition.rules.RuleSet;
import net.openvision.util.Pair;

public class MusicalTexturer implements SequenceFiller {

	public static class ElementTree {
		public static class Node implements Iterable<Node> {
			private List<Node> children;
			private Element content;

			public Node(Element content) {
				this.content = content;
			}

			public void addChild(Node e) {
				children.add(e);
			}

			public Element getElement() {
				return content;
			}

			public Iterator<Node> iterator() {
				return children.iterator();
			}
		}

		private Node root;

		public ElementTree(Node root) {
			this.root = root;
		}
	}
	
	public static class Link {
		private Element current;
		private List<Element> next;
		
		public Link(Element current) {
			this.current = current;
			next = new ArrayList<Element>();
		}
		
		public Element getCurrent() {
			return current;
		}
		
		public List<Element> getNext() {
			return next;
		}
	}

	private Node getTree(Element element,
			List<Collection<Element>> constructed, int index, RuleSet rset) {
		Node result = new Node(element);
		if (index < constructed.size()) {
			for (Element e : constructed.get(index)) {
				if (!Double.isInfinite(rset.getLocalRules()
						.evaluate(element, e))) {
					result.addChild(getTree(e, constructed, index + 1, rset));
				}
			}
		}
		return result;
	}
	
	
	List<Collection<Element>> _constructed;
	List<byte[][]> _result;
	Map<Pair<Integer, Element>, List<Element>> _links;
	RuleSet _rset;
	
	private void progress(int collectionIndex, Element e, byte[][] sequence) {
		
		if (collectionIndex == _constructed.size()-1) { // last element, recursion termination
			System.arraycopy(e.getValue().getBytes(), 0, sequence[collectionIndex], 0, e.getSize());
			_result.add(sequence);
			//System.out.print("f");
			return;
		}
		
		List<Element> next = _links.get(new Pair<Integer, Element>(collectionIndex, e));
		if (next == null) {
			next = new ArrayList<Element>();
			for (Element n:_constructed.get(collectionIndex+1)) {
				if (!Double.isInfinite(_rset.getLocalRules().evaluate(e, n))) {
					next.add(n);
				}
			}
			_links.put(new Pair<Integer, Element>(collectionIndex, e), next);
		}
		System.arraycopy(e.getValue().getBytes(), 0, sequence[collectionIndex], 0, e.getSize());
		if (next.size() > 0) {
			progress(collectionIndex+1, next.get(0), sequence); 
			for (int i=1;i<next.size();i++) {
				progress(collectionIndex+1, next.get(i), sequence.clone()); 
			}
		}
		
	}

	@Override
	public MultiSequence complete(MultiSequence sequence, RuleSet rset) {
		ConstructionRule construction = rset.getConstructionRule();
		_constructed = new ArrayList<Collection<Element>>();
		_result = new ArrayList<byte[][]>();
		_links = new HashMap<Pair<Integer, Element>, List<Element>>();
		_rset = rset;
		
		for (Element e : sequence) {
			Collection<Element> cmp = construction.complete(e);
			_constructed.add(cmp);
			System.out.println(cmp);
		}
		
		for (Element e : _constructed.get(0)) {
			progress(0, e, new byte[_constructed.size()][sequence.getElementSize()]);
		}
		
		/*Map<Element, List<Element>> links = new HashMap<Element, List<Element>>();
		for (int i=0; i < constructed.size()-1; i++) {
			for (Element p:constructed.get(i)) {
				ArrayList<Element> next = new ArrayList<Element>();
				links.put(p, next);
				for (Element n:constructed.get(i+1)) {
					if (!Double.isInfinite(rset.getLocalRules().evaluate(p, n))) {
						next.add(n);
					}
				}
			}
		}*/
		
		// compute size
		
		

		return null;
	}

	@Override
	public List<byte[][]> getResult() {
		return _result;
	}

}
